﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using EzDesk.Core.Data;
using EzDesk.Core.Model;
using EzDesk.Utility;
using EzDesk.Utility.Extensions;
using EzDesk.Utility.EzML;
using EzDesk.Utility.Json;
using EzDesk.Core.Interface;
using EzDesk.Utility.Log;
using EzDesk.Utility.Web.Extensions;

namespace EzDesk.Core.ServiceImpl.Services
{
    public partial class CoreService : ICoreService
    {
        #region [ Send Feeds Methods ]

        private void addUserToFeed(Guid userId, Guid feedId, int feedTypeValue, string feedDataEx, DateTime feedCreateOn, DateTime? expireOn, EzDeskDatabaseContainer container)
        {
            container.Feed_RecipientSet.AddObject(
                new Feed_Recipient()
                {
                    Id = Guid.NewGuid(),
                    RecipientId = userId,
                    FeedId = feedId,
                    FeedDataEx = feedDataEx,
                    IsRead = false,
                    IsAvailable = true,
                    IsMark = false,
                    LastChangeOn = feedCreateOn,
                    FeedTypeValue = feedTypeValue,
                    FeedUpdateOn = feedCreateOn,
                    ExpireOn = expireOn,
                });
        }

        private Dictionary<Guid, string> getPersonalFeedsData(Guid userId, List<Guid> feedIds)
        {
            return EzDeskDatabaseContainer.Go(
                container => container.Feed_RecipientSet
                                 .Where(p => p.RecipientId == userId && feedIds.Contains(p.FeedId))
                                 .ToDictionary(p => p.FeedId, p => p.FeedDataEx)
                );
        }
        private string getPersonalFeedData(Guid userId, Guid feedId)
        {
            return EzDeskDatabaseContainer.Go(
                container => container.Feed_RecipientSet
                                 .Where(p => p.RecipientId == userId && p.FeedId == feedId)
                                 .Select(p => p.FeedDataEx)
                                 .SingleOrDefault());
        }

        public List<Guid> getFeedRecord(Guid identity, string templateName, Guid? userId)
        {
            return EzDeskDatabaseContainer.Go(
                container =>
                {
                    var query = container.Feed_RecordSet
                        .Where(p => p.TemplateName == templateName && p.Identity == identity);
                    if (userId.HasValue)
                    {
                        query = query.Where(p => p.UserId == userId.Value);
                    }
                    return query.Select(p => p.FeedId).Distinct().ToList();
                });
        }
        
        private Guid innerSendFeed(FeedTemplate template, string feedData, string memberString, Guid? senderId, string ignoreUsers, bool isAvailable, bool isDisplayInMainPanel)
        {
            return EzDeskDatabaseContainer.GoAndSave(
                container =>
                    {
                        var templateId = template.Id;
                        var feedType = template.DefaultTypeValue;

                        // 验证FeedData和模板的匹配
                        try
                        {
                            validateFeedData(template, feedData);
                        }
                        catch (Exception ex)
                        {
                            throw new Exception("FeedData Error!", ex);
                        }

                        //保留 <br/>和&nbsp;
                        feedData = replaceEzTarget(feedData);
                        // 处理掉feedData中的html, 只留下空格
                        feedData = feedData.RemoveHtml(false);
                        //恢复ez标记
                        feedData = recoveryEzTarget(feedData);

                        var newFeedId = Guid.NewGuid();
                        var now = DateTime.Now;

                        // 创建 Feed
                        container.FeedSet.AddObject(
                            new Feed
                                {
                                    Id = newFeedId,
                                    TemplateId = templateId,
                                    CreateOn = now,
                                    UpdateOn = now,
                                    LastChangeOn = now,
                                    FeedData = feedData,
                                    IsRead = false,
                                    IsAvailable = isAvailable,
                                    IsDisplayInMainPanel = isDisplayInMainPanel,
                                    OwnerId = senderId,
                                    Icon = template.TemplateIconName,
                                }
                            );
                        DateTime? expireOn = template.Period >= 0
                                           ? new DateTime?(now.AddMinutes(template.Period))
                                           : null;

                        // 把Feed发送给要收到的人
                        var allUserIds = getAllUserIdsByMemberString(memberString);
                        var ignoreUserIds = getAllUserIdsByMemberString(ignoreUsers);

                        allUserIds
                            .Where(p => !ignoreUserIds.Contains(p))
                            .Foreach(userId =>
                                     addUserToFeed(userId, newFeedId, feedType, null, now, expireOn, container)
                            );

                        return newFeedId;
                    });

        }


        Guid ICoreService.SendFeed(
            string templateName, string feedData, string memberString,
            Guid? senderId, string ignoreUsers, bool isAvailable, bool isDisplayInMainPanel
            )
        {
            return EzDeskDatabaseContainer.Go(
                container =>
                    {
                        // 消息模板
                        var template = container.FeedTemplateSet.FirstOrDefault(p => p.TemplateName == templateName);
                        if (template == null)
                        {
                            throw new Exception("Feed Template not Found!");
                        }
                        return innerSendFeed(template, feedData, memberString, senderId, ignoreUsers, isAvailable, isDisplayInMainPanel);
                    });
        }




        private void validateFeedData(FeedTemplate template, string feedData)
        {
            EzMLHelper.MergeDataToEzML(template.Title, feedData);
            EzMLHelper.MergeDataToEzML(template.Body, feedData);
        }


        void ICoreService.UpdatePersonalFeedData(Guid feedId, string memberstring, string feedDataEx)
        {
            EzDeskDatabaseContainer.GoAndSave(
                container =>
                    {
                        var userIds = self.GetAllUserIdsByMemberString(memberstring);
                        foreach (var userId in userIds)
                        {
                            // 合并FeedData
                            string originalData = getPersonalFeedData(userId, feedId);
                            var feedDataToUpdate = merageExtraFeedData(originalData, feedDataEx);
                            if (feedDataToUpdate == originalData) continue;

                            var item = container.Feed_RecipientSet
                                .Where(p => p.RecipientId == userId && p.FeedId == feedId)
                                .SingleOrDefault();
                            if (item != null)
                            {
                                item.FeedDataEx = feedDataEx;
                                item.LastChangeOn = DateTime.Now;
                            }
                            // 更新数据后，设为未读
                            setFeedIsRead(feedId, userId.ToString(), false);
                        }
                    });
            
        }



        void ICoreService.UpdatePersonalFeedType(Guid feedId, string memberstring, string feedTypeStr)
        {
            EzDeskDatabaseContainer.GoAndSave(
                container =>
                    {
                        var feedTypeValue = FeedTypeDictionary.StringToIntType(feedTypeStr);
                        var userIds = self.GetAllUserIdsByMemberString(memberstring);
                        foreach (var userId in userIds)
                        {
                            var item = container.Feed_RecipientSet
                                          .Where(p => p.RecipientId == userId && p.FeedId == feedId)
                                          .SingleOrDefault();
                            if (item != null)
                            {
                                item.FeedTypeValue = feedTypeValue;
                                item.LastChangeOn = DateTime.Now;
                            }
                            // 更新数据后，设为未读
                            setFeedIsRead(feedId, userId.ToString(), false);
                        }
                    });
        }

        #region [ private methods - Set Feed Properties Methods]

        void updateFeedData(Guid[] feedIds, string newFeedData)
        {
            EzDeskDatabaseContainer.GoAndSave(
                container =>
                    {
                        var newUpdateOn = DateTime.Now;
                        foreach (var feedId in feedIds)
                        {
                            var id = feedId;
                            var feed = container.FeedSet.FirstOrDefault(p => p.Id == id);

                            var feedTemplate =
                                container.FeedTemplateSet.FirstOrDefault(p => p.Id == feed.TemplateId);

                            string newFeedDataToUpdate = merageNewFeedData(feed.FeedData, newFeedData);
                            validateFeedData(feedTemplate, newFeedDataToUpdate);
                            feed.FeedData = newFeedDataToUpdate;
                            feed.UpdateOn = newUpdateOn;
                            // 更新了数据 设为未读
                            feed.IsRead = false;
                        }

                        // 更新这些所有feed的feed_recipient的FeedUpdateOn
                        var feedRecipients = container.Feed_RecipientSet.Where(p => feedIds.Contains(p.FeedId));
                        foreach (var feedRecipient in feedRecipients)
                        {
                            feedRecipient.FeedUpdateOn = newUpdateOn;
                        }
                    });
        }

        void setFeedIsRead(Guid feedId, string memberString, bool isRead)
        {
            setFeedIsRead(
                convertSingleIdToIdCollection(feedId),
                memberString,
                isRead);
        }
        void setFeedIsRead(string feedIdStrs, string memberString, bool isRead)
        {
            setFeedIsRead(
                convertIdStrsToIdCollection(feedIdStrs),
                memberString,
                isRead
                );
        }

        void setFeedIsRead(Guid[] feedIds, string memberString, bool isRead)
        {
            EzDeskDatabaseContainer.GoAndSave(
                container =>
                {
                    if (feedIds.Length == 0) return;
                    var userIds = self.GetAllUserIdsByMemberString(memberString);
                    foreach (var feedId in feedIds)
                    {
                        foreach (var userId in userIds)
                        {
                            var feedRecipient =
                                      container.Feed_RecipientSet.FirstOrDefault(
                                          p => p.FeedId == feedId && p.RecipientId == userId);
                            if (feedRecipient == null) continue;
                            feedRecipient.IsRead = isRead;
                            var changeOn = DateTime.Now;
                            feedRecipient.LastChangeOn = changeOn;
                            feedRecipient.FeedUpdateOn = changeOn;
                        }
                    }
                });
        }


        void setFeedIsAvailable(Guid feedId, string memberString, bool isAvailable)
        {
            setFeedIsAvailable(
                convertSingleIdToIdCollection(feedId),
                memberString,
                isAvailable
                );
        }


        void setFeedIsAvailable(string feedIdStrs, string memberString, bool isAvailable)
        {
            setFeedIsAvailable(
                convertIdStrsToIdCollection(feedIdStrs),
                memberString,
                isAvailable
                );
        }

        void setFeedIsAvailable(Guid[] feedIds, string memberString, bool isAvailable)
        {
            EzDeskDatabaseContainer.GoAndSave(
                container =>
                {
                    if (feedIds.Length == 0) return;
                    var userIds = self.GetAllUserIdsByMemberString(memberString);
                    var feedRecipients =
                        container.Feed_RecipientSet.Where(
                            p => userIds.Contains(p.RecipientId) && feedIds.Contains(p.FeedId));
                    foreach (var feedRecipient in feedRecipients)
                    {
                        feedRecipient.IsAvailable = isAvailable;
                        var changeOn = DateTime.Now;
                        feedRecipient.LastChangeOn = changeOn;
                        feedRecipient.FeedUpdateOn = changeOn;
                    }
                });
        }

        void setFeedIsMark(string feedIdStrs, string memberString, bool isMark)
        {
            setFeedIsMark(
                convertIdStrsToIdCollection(feedIdStrs),
                memberString,
                isMark
                );
        }

        void setFeedIsMark(Guid feedId, string memberString, bool isMark)
        {
            setFeedIsMark(
                convertSingleIdToIdCollection(feedId),
                memberString,
                isMark);
        }

        void setFeedIsMark(Guid[] feedIds, string memberString, bool isMark)
        {
            EzDeskDatabaseContainer.GoAndSave(
                container =>
                {
                    if (feedIds.Length == 0) return;
                    var userIds = self.GetAllUserIdsByMemberString(memberString);
                    foreach (var feedId in feedIds)
                    {
                        foreach (var userId in userIds)
                        {
                            var feedRecipient =
                                      container.Feed_RecipientSet.FirstOrDefault(
                                          p => p.FeedId == feedId && p.RecipientId == userId);
                            if (feedRecipient == null) continue;
                            feedRecipient.IsMark = isMark;
                            var changeOn = DateTime.Now;
                            feedRecipient.LastChangeOn = changeOn;
                            feedRecipient.FeedUpdateOn = changeOn;
                        }
                    }
                });
        }

        private string replaceEzTarget(string str)
        {
            return str.Replace(@"<br />", @"[br /]")
                                    .Replace(@"<p>", @"[p]")
                                    .Replace(@"</p>", @"[/p]");
        }

        private string recoveryEzTarget(string str)
        {
            return str.Replace(@"[br /]", @"<br />")
                                 .Replace(@"[p]", @"<p>")
                                 .Replace(@"[/p]", @"</p>");
        }

        #endregion
        #endregion

        #region [ Update Feeds Methods]

        public void UpdateFeedRecipients(Guid feedId, string memerstring)
        {
            EzDeskDatabaseContainer.Go(
                container =>
                    {
                        var userIds = self.GetAllUserIdsByMemberString(memerstring);
                        var feed = container.FeedSet.First(p => p.Id == feedId);
                        var template = container.FeedTemplateSet.First(p => p.Id == feed.TemplateId);
                        DateTime? expireOn = template.Period >= 0
                           ? new DateTime?(DateTime.Now.AddMinutes(template.Period))
                           : null;
                        var recipients = container.Feed_RecipientSet.Where(p => p.FeedId == feedId).ToList();
                        foreach (var userId in userIds)
                        {
                            var recipient = recipients.FirstOrDefault(p => p.RecipientId == userId);
                            if (recipient == null)
                            {
                                addUserToFeed(userId, feedId, feed.FeedType, null, DateTime.Now, expireOn, container);
                            }
                            else
                            {
                                recipients.Remove(recipient);
                            }
                        }
                        // 删除已经不存在的Feed接受者
                        foreach (var feedRecipient in recipients)
                        {
                            container.Feed_RecipientSet.DeleteObject(feedRecipient);
                        }
                        container.SaveChanges();
                    });
        }

        void ICoreService.UpdateFeedData(Guid feedId, string newFeedData)
        {
            updateFeedData(
                convertSingleIdToIdCollection(feedId),
                newFeedData
                );
        }

        void ICoreService.UpdateFeedData(string feedIdStrs, string newFeedData)
        {
            updateFeedData(
                convertIdStrsToIdCollection(feedIdStrs),
                newFeedData
                );
        }

        void ICoreService.UpdateFeedStatus(List<FeedStatus> feedStatus, string memberString)
        {
            foreach (var feed in feedStatus)
            {
                try
                {
                    if (feed.IsRead.HasValue) setFeedIsRead(feed.FeedId, memberString, feed.IsRead.Value);
                    if (feed.IsAvailable.HasValue) setFeedIsAvailable(feed.FeedId, memberString, feed.IsAvailable.Value);
                    if (feed.IsMark.HasValue) setFeedIsMark(feed.FeedId, memberString, feed.IsMark.Value);
                }
                catch (Exception ex)
                {
                    LogEx.ERROR("Core - FeedUpdate", "更新Feed时失败", ex);
                    continue;
                }
            }
        }

        private string merageNewFeedData(string originalFeedData, string newFeedData)
        {
            IDictionary<string, string> orignalData = JsonHelper.JsonToDictionary(originalFeedData);
            IDictionary<string, string> newData = JsonHelper.JsonToDictionary(newFeedData);
            // 更新 原有字段信息
            foreach (var item in newData)
            {
                if (orignalData.ContainsKey(item.Key))
                {
                    orignalData[item.Key] = item.Value;
                }
                else
                {
                    //todo: error
                    //throw new Exception("Feed Data Error! " + item.Key + " not found!");
                }
            }
            return JsonHelper.ConvertToJson(orignalData);
        }

        private string merageExtraFeedData(string originalFeedData, string newFeedData)
        {
            IDictionary<string, string> orignalData = JsonHelper.JsonToDictionary(originalFeedData);
            IDictionary<string, string> newData = JsonHelper.JsonToDictionary(newFeedData);

            foreach (var item in newData)
            {
                if (orignalData.ContainsKey(item.Key))
                {
                    orignalData[item.Key] = item.Value;
                }
                else
                {
                    orignalData.Add(item.Key, item.Value);
                }
            }
            return JsonHelper.ConvertToJson(orignalData);
        }
        #endregion

        #region [ Delete Methods ]
        void ICoreService.DeleteFeed(Guid feedId, int type)
        {
            throw  new NotImplementedException();
        }
        #endregion


        #region [ Get Feeds ]

        List<Feed> ICoreService.GetFeeds(Guid userId, DateTime? from, DateTime? to, int maxCount, bool includeNotification, bool? isRead, bool? isAvailable, bool? isMark)
        {
            //LogEx.TRACE("CoreService_Feeds", "GetFeeds开始");
            var result = EzDeskDatabaseContainer.Go(
                          container =>
                          {
                              // 根据FeedUpdateOn把数据给取出来
                              var query = container.Feed_RecipientSet.Where(p => p.RecipientId == userId &&
                                  (!p.ExpireOn.HasValue || p.ExpireOn > DateTime.Now));

                              if (!includeNotification)
                              {
                                  query = query.Where(p => p.FeedTypeValue != FeedTypeDictionary.StringToIntType("notification"));
                              }
                              if (isRead.HasValue)
                              {
                                  query = query.Where(p => p.IsRead == isRead.Value);
                              }
                              if (isAvailable.HasValue)
                              {
                                  query = query.Where(p => p.IsAvailable == isAvailable.Value);
                              }
                              if (isMark.HasValue)
                              {
                                  query = query.Where(p => p.IsMark == isMark.Value);
                              }
                              if (from.HasValue)
                              {
                                  query = query.Where(p => p.FeedUpdateOn > from);
                              }
                              if (to.HasValue)
                              {
                                  query = query.Where(p => p.FeedUpdateOn < to);
                              }

                              query = query
                                  .OrderByDescending(p => p.FeedUpdateOn)
                                  .Take(maxCount);

                              var feedRecipientList = query.ToList();

                              var feedIdList = feedRecipientList.Select(p => p.FeedId);
                              var feedList = container.FeedSet
                                  .Where(p => feedIdList.Contains(p.Id)).ToList()
                                  .Select(feed =>
                                  {
                                      var feedRecipient = feedRecipientList.FirstOrDefault(item => item.FeedId == feed.Id);
                                      feed.FeedType = feedRecipient.FeedTypeValue;
                                      feed.LastChangeOn = feedRecipient.LastChangeOn;
                                      feed.IsRead = feedRecipient.IsRead;
                                      feed.IsAvailable = feedRecipient.IsAvailable;
                                      feed.IsMark = feedRecipient.IsMark;
                                      feed.PersonalData = feedRecipient.FeedDataEx;
                                      return feed;
                                  }).ToList();
                              
                              // 把那些只需要显示一次的Feeds给disable掉
                              var notDisplayInMainPanelList =
                                  feedList.Where(p => !p.IsDisplayInMainPanel && p.IsAvailable.GetValueOrDefault(true)).Select(p => p.Id).ToArray();
                              setFeedIsAvailable(notDisplayInMainPanelList, "everyone", false);
                              
                              return feedList;
                          });
            //LogEx.TRACE("CoreService_Feeds", "GetFeeds结束");
            return result;
        }

        #endregion

        string ICoreService.GetPersonalFeedData(Guid userId, Guid feedId)
        {
            return getPersonalFeedData(userId, feedId);
        }

        Dictionary<Guid, string> ICoreService.GetPersonalFeedsData(Guid userId, List<Guid> feedIds)
        {
            return getPersonalFeedsData(userId, feedIds);
        }


        #region [ Feed Records ]
        public void SetFeedRecord(Guid identity, string templateName, string memberstring, Guid feedId, string ignoreUsers)
        {
            EzDeskDatabaseContainer.GoAndSave(
                container =>
                    {
                        var allUserIds = getAllUserIdsByMemberString(memberstring);
                        var ignoreUserIds = getAllUserIdsByMemberString(ignoreUsers);

                        allUserIds
                            .Where(p => !ignoreUserIds.Contains(p))
                            .Foreach(
                                userId =>
                                    {
                                        var existedRecord =
                                            container.Feed_RecordSet.FirstOrDefault(
                                                p => p.UserId == userId && p.Identity == identity && p.TemplateName == templateName);
                                        if (existedRecord != null)
                                        {
                                            container.Feed_RecordSet.DeleteObject(existedRecord);
                                            container.SaveChanges();
                                        }
                                        container.Feed_RecordSet.AddObject(
                                            new Feed_Record
                                                {
                                                    FeedId = feedId,
                                                    TemplateName = templateName,
                                                    Identity = identity,
                                                    UserId = userId
                                                }
                                            );
                                    });
                    });
        }

        public string GetFeedRecord(Guid identity, string templateName, string userIds)
        {
            var allUserIds = getAllUserIdsByMemberString(userIds);
            List<Guid> feedIdList = new List<Guid>();

            if (allUserIds.Count == 0)
            {
                // 如果没有指定UserId那么就从所有用户里面找
                feedIdList = getFeedRecord(identity, templateName, null);
            }
            else
            {
                IEnumerable<Guid> feedList = feedIdList;
                allUserIds.Foreach(
                    userId =>
                    {
                        feedList = getFeedRecord(identity, templateName, userId);
                    });
                feedIdList = feedList.ToList();
            }
            return feedIdList.Join(",");

        }

        

        #endregion





    }
}
